#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <pthread.h>

#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int sockfd; // 网络套接字

void *recv_task(void *arg){  // 接收数据的
    char buf[1024] = "";
    char ip[16] = "";
    while(1){
        bzero(buf, 1024);
        bzero(ip, 16);

        // 定义接收数据中的地址信息变量
        struct sockaddr_in data_addr;  
        socklen_t s_len = sizeof(data_addr);

        // 接收数据
        ssize_t data_len = recvfrom(sockfd, buf, 1024, 0, (struct sockaddr *)&data_addr, &s_len);
        if(data_len > 0){
            // 将uint32_t 数值转化 ip字符串
            inet_ntop(AF_INET, &data_addr.sin_addr.s_addr, ip, 16);
            printf("%s: %s\n", ip, buf);
        }
    }
    return NULL;
}

int main(int argc, char const *argv[])
{
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0) {
        perror("socket");
        return -1;
    }

    // 向多播地址中添加本地IP
    struct ip_mreq m;
    m.imr_multiaddr.s_addr = inet_addr("224.5.5.5"); // 多播IP
    m.imr_interface.s_addr = INADDR_ANY; // 本地任意网卡(IP)地址
    if( setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &m, sizeof(m)) != 0){
        printf("设置多播失败!\n");
        close(sockfd);
        return -1;
    }

    // 绑定
    // 定义绑定的地址信息
    struct sockaddr_in l_addr = {
        .sin_family = AF_INET,
        .sin_port = htons(7433),
        .sin_addr.s_addr = INADDR_ANY
    };
    // 绑定并验证
    if(bind(sockfd, (struct sockaddr *)&l_addr, sizeof(l_addr)) != 0){
        perror("bind");
        close(sockfd);
        return -1;
    }

    printf("启动中...\n");

    // 创建接收网络数据包的数据的线程
    pthread_t tid;
    pthread_create(&tid, NULL, recv_task, NULL);

    // 从键盘接收数据并发送
    char buf[1024] = "";
    // 定义发送目标（多播+7433端口）
    struct sockaddr_in dst_addr = {
        .sin_family = AF_INET,
        .sin_port = htons(7433),
        .sin_addr.s_addr = inet_addr("224.5.5.5")
    };

    while(1){
        bzero(buf, 1024);
        fgets(buf, 1024, stdin);
        buf[strlen(buf)- 1] = 0;

        sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr));
        if(strncmp(buf, "exit", 4) == 0) break;
    }

    pthread_cancel(tid);
    pthread_join(tid, NULL);
    printf("---over--\n");
    close(sockfd);

    return 0;
}
